package org.cainiao.portal.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.cainiao.api.lark.dto.request.docs.apireference.ObtainAllBlocksOfDocumentRequest;
import org.cainiao.api.lark.dto.request.docs.space.folder.ListItemsInFolderRequest;
import org.cainiao.api.lark.dto.response.LarkDataResponse;
import org.cainiao.api.lark.dto.response.docs.docs.apireference.document.LarkBlockPage;
import org.cainiao.api.lark.dto.response.docs.space.folder.LarkFile;
import org.cainiao.api.lark.dto.response.docs.space.folder.LarkFilePage;
import org.cainiao.api.lark.dto.response.docs.space.folder.LarkFolderMeta;
import org.cainiao.api.lark.imperative.LarkApi;
import org.cainiao.common.exception.BusinessException;
import org.cainiao.portal.dao.service.DocumentMapperService;
import org.cainiao.portal.dto.response.TreeNode;
import org.cainiao.portal.dto.response.document.CnBlock;
import org.cainiao.portal.dto.response.document.LarkFolderDocument;
import org.cainiao.portal.entity.document.Document;
import org.cainiao.portal.service.DocumentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.http.CacheControl;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static org.cainiao.portal.entity.document.Document.DocumentTypeEnum;
import static org.cainiao.portal.util.Util.getSubstringAfterUnderscore;

/**
 * <br />
 * <p>
 * Author: Cai Niao(wdhlzd@163.com)<br />
 */
@Service
@RequiredArgsConstructor
public class DocumentServiceImpl implements DocumentService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DocumentServiceImpl.class);

    private static final String ORDER_PREFIX_SEPARATOR = "_";

    private final LarkApi larkApi;
    private final DocumentMapperService documentMapperService;

    @Override
    public List<TreeNode> publishedDocuments(Long knowledgePointId) {
        List<Document> documents = documentMapperService
            .findDocumentByKnowledgePointIdAndPublished(knowledgePointId, true);
        Map<Long, TreeNode> idTreeNodeMap = new HashMap<>();
        for (Document document : documents) {
            idTreeNodeMap.put(document.getId(), TreeNode.builder()
                .key(document.getId().toString()).title(document.getName()).type(document.getType())
                .externalId(document.getExternalId())
                .isLeaf(!DocumentTypeEnum.LARK_FOLDER.equals(document.getType())).build());
        }

        List<TreeNode> roots = new ArrayList<>();
        for (Document document : documents) {
            TreeNode treeNode = idTreeNodeMap.get(document.getId());
            Long parentId = document.getParentId();
            if (parentId == null) {
                roots.add(treeNode);
            } else {
                TreeNode parent = idTreeNodeMap.get(parentId);
                if (parent == null) {
                    roots.add(treeNode);
                } else {
                    List<TreeNode> children = parent.getChildren();
                    if (children == null) {
                        children = new ArrayList<>();
                        children.add(treeNode);
                        parent.setChildren(children);
                        parent.setLeaf(false);
                    } else {
                        children.add(treeNode);
                    }
                }
            }
        }
        return roots;
    }

    @Override
    public LarkFolderDocument createLarkFolderDocument(String ownerOpenId, Long parentId, String folderToken) {
        if (documentMapperService.existsByOwnerOpenIdAndParentIdAndExternalId(ownerOpenId, parentId, folderToken)) {
            throw new BusinessException("该目录下已存在该飞书同步笔记");
        }

        LarkDataResponse<LarkFilePage> larkDataResponse = larkApi
            .docs().space().folder().listItemsInFolder(ListItemsInFolderRequest.builder()
                .folderToken(folderToken)
                .build()).getBody();
        Assert.notNull(larkDataResponse, "larkDataResponse cannot be null");
        if (larkDataResponse.getCode() != 0) {
            LOGGER.error("调用飞书接口失败，code = {}; msg = {}, logId = {}",
                larkDataResponse.getCode(), larkDataResponse.getMsg(), larkDataResponse.getError().getLogId());
            throw new BusinessException(String.format("调用飞书接口失败：%s", larkDataResponse.getMsg()));
        }

        Document document = Document.builder().ownerOpenId(ownerOpenId)
            .parentId(parentId).type(DocumentTypeEnum.LARK_FOLDER).externalId(folderToken)
            .createdBy(ownerOpenId).updatedBy(ownerOpenId)
            .build();
        if (!documentMapperService.save(document)) {
            throw new BusinessException("save Document fail");
        }
        return LarkFolderDocument.fromDocument(document, larkDataResponse.getData());
    }

    @Override
    public List<Document> getChildren(Long documentId, String systemUserId) {
        return documentMapperService.findByOwnerOpenIdAndParentId(systemUserId, documentId).stream()
            .peek(document -> {
                if (DocumentTypeEnum.LARK_FOLDER.equals(document.getType())) {
                    LarkDataResponse<LarkFolderMeta> larkFolderMetaLarkDataResponse = larkApi
                        .docs().space().folder().getFolderMeta(document.getExternalId()).getBody();
                    Assert.notNull(larkFolderMetaLarkDataResponse, "larkFolderMetaLarkDataResponse cannot be null");
                    document.setName(larkFolderMetaLarkDataResponse.getData().getName());
                }
            }).toList();
    }

    @Override
    public IPage<TreeNode> getLarkChildren(String token, int current, int size) {
        LarkFilePage larkFilePage = getLarkChildrenPage(token);
        List<LarkFile> larkFiles = larkFilePage.getFiles();
        List<TreeNode> treeNodes = larkFiles.stream().map(larkFile -> {
            String fileToken = larkFile.getToken();
            boolean isFolder = "folder".equals(larkFile.getType());
            return TreeNode.builder()
                .key(fileToken)
                .title(larkFile.getName())
                .externalId(fileToken)
                .isLeaf(!isFolder)
                .type(isFolder ? DocumentTypeEnum.LARK_FOLDER : DocumentTypeEnum.LARK_DOCUMENT)
                .build();
        }).toList();
        IPage<TreeNode> page = new Page<>(current, size);
        page.setRecords(treeNodes);
        return page;
    }

    private LarkFilePage getLarkChildrenPage(String token) {
        LarkDataResponse<LarkFilePage> larkDataResponse = larkApi
            .docs().space().folder().listItemsInFolder(ListItemsInFolderRequest.builder()
                .folderToken(token)
                .build()).getBody();
        Assert.notNull(larkDataResponse, "larkDataResponse cannot be null");
        LarkFilePage larkFilePage = larkDataResponse.getData();
        List<LarkFile> larkFiles = larkFilePage.getFiles();
        larkFiles.sort(Comparator.comparing(LarkFile::getName));
        larkFiles.sort((c1, c2) -> getOrder(c1.getName()).compareTo(getOrder(c2.getName())));
        larkFiles.forEach(larkFile -> larkFile.setName(getSubstringAfterUnderscore(larkFile.getName())));
        return larkFilePage;
    }

    private Integer getOrder(String name) {
        try {
            return Integer.parseInt(name.split(ORDER_PREFIX_SEPARATOR)[0]);
        } catch (Exception e) {
            return 9999;
        }
    }

    @Override
    public List<CnBlock> getLarkBlocks(String documentId) {
        LarkDataResponse<LarkBlockPage> larkDataResponse = larkApi.docs().docsApi().apiReference().document()
            .obtainAllBlocksOfDocument(documentId, ObtainAllBlocksOfDocumentRequest.builder().build()).getBody();
        Assert.notNull(larkDataResponse, "larkDataResponse cannot be null");
        return CnBlock.fromLarkBlocks(larkDataResponse.getData().getItems());
    }

    @Override
    public ResponseEntity<Resource> whiteboardImage(String whiteboardId) {
        ResponseEntity<Resource> responseEntity = larkApi.docs().board().thumbnail().whiteboardImage(whiteboardId);
        return ResponseEntity.ok()
            .cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic())
            .headers(responseEntity.getHeaders())
            .body(responseEntity.getBody());
    }
}
